﻿//===================================================================================
// Copyright (c) Ryan Garrett.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===================================================================================

using System;
using System.Collections.Generic;
using Caffeine.Core.Metadata;

namespace Caffeine.Core
{
    /// <summary>
    /// Provides a base class for all scalar model properties
    /// </summary>
    public abstract class ModelScalarPropertyBase : ModelPropertyBase
    {
        #region State

        private Boolean _isDefault;
        private readonly String _isDefaultWrappedName;
        private readonly String _committedValueWrappedName;

        #endregion

        #region Constructors

        internal ModelScalarPropertyBase(ModelBase parent, String name, Nullable<ModelPropertyBehaviors> behavior, Boolean isWrapped)
            : base(parent, name, behavior, isWrapped)
        {
            _isDefault = true;

            if (IsWrapped)
            {
                _isDefaultWrappedName = String.Format("{0}{1}", WrappedPropertyName, IsDefaultPropertyName);
                _committedValueWrappedName = String.Format("{0}{1}", WrappedPropertyName, CommittedValuePropertyName);
            }
        }

        #endregion

        #region Public

        /// <summary>
        /// Gets whether or not this property is currently set to its default value
        /// </summary>
        public Boolean IsDefault
        {
            get
            {
                return _isDefault;
            }
            protected set
            {
                if (_isDefault != value)
                {
                    _isDefault = value;
                    RaiseNotifyPropertyChanged("IsDefault");
                    Parent.RaiseOnPropertyIsDefaultChanged(this);
                }
            }
        }

        /// <summary>
        /// Gets whether or not this property contains child models
        /// </summary>
        public override Boolean IsModelProperty
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Will always return false
        /// </summary>
        public override sealed Boolean IsModelCollectionProperty
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets or sets whether or not to synchronize values only when this property is committed
        /// </summary>
        public Boolean UpdateSyncOnCommit
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets whether or not this property can be synced to. If false, this property will not accept value changes from
        /// source properties. 
        /// </summary>
        public Boolean AllowSync
        {
            get;
            set;
        }

        /// <summary>
        /// Raise a property change notification event on this model property
        /// </summary>
        /// <param name="propertyName">Name of property to raise event for</param>
        public override void RaiseNotifyPropertyChanged(String propertyName)
        {
            base.RaiseNotifyPropertyChanged(propertyName);

            if (IsWrapped)
            {
                switch (propertyName)
                {
                    case ValuePropertyName:
                        Parent.RaisePropertyChanged(WrappedPropertyName);
                        break;
                    case IsDefaultPropertyName:
                        Parent.RaisePropertyChanged(IsDefaultWrappedName);
                        break;
                    case CommittedValuePropertyName:
                        Parent.RaisePropertyChanged(CommittedValueWrappedName);
                        break;
                }
            }
        }

        #endregion

        #region Internal

        internal String IsDefaultWrappedName
        {
            get
            {
                return _isDefaultWrappedName;
            }
        }

        internal String CommittedValueWrappedName
        {
            get
            {
                return _committedValueWrappedName;
            }
        }

        internal abstract ModelBase GetModelValue();

        internal abstract ModelBase GetCommittedModelValue();

        internal abstract ModelScalarPropertyMetadata GetMetadata();

        internal abstract Object GetValueForTesting();

        internal abstract Object GetCommittedValueForTesting();

        internal abstract Object GetPropertySyncForTesting();

        internal abstract Object GetCompareMethodForTesting();

        internal abstract Object GetValidationMethodForTesting();

        internal void ApplyScalarPropertyMetadata(ModelScalarPropertyMetadata metadata, IModelFactory factory)
        {
            ApplyPropertyMetadata(metadata);
            OnApplyScalarPropertyMetadata(metadata, factory);
            UpdateSyncOnCommit = metadata.UpdateSyncOnCommit;
            AllowSync = metadata.AllowSync;
            _isDefault = metadata.IsDefault;
        }

        #endregion

        #region Protected

        protected abstract void OnApplyScalarPropertyMetadata(ModelScalarPropertyMetadata metadata, IModelFactory factory);

        #endregion

        #region Private

        private const String IsDefaultPropertyName = "IsDefault";

        private const String ValuePropertyName = "Value";

        private const String CommittedValuePropertyName = "CommittedValue";

        #endregion
    }
}
